from dataclasses import dataclass
from dataclasses import asdict
from datetime import timedelta
from datetime import datetime
from typing import Union
from typing import Dict
from typing import List
from typing import Any
from typing import Optional
from functools import lru_cache

from jose import jwt
from passlib.context import CryptContext
from starlette.config import Config


@lru_cache()
def get_config(key: str, path: str = '.env'):
    config = Config(path)
    return config.get(key)


pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


def verify_password(plain_password: str, hashed_password: str) -> bool:
    """
    验证明文密码 vs hash密码
    :param plain_password: 明文密码
    :param hashed_password: hash密码
    :return:
    """
    return pwd_context.verify(plain_password, hashed_password)


def get_password_hash(password: str) -> str:
    """
    加密明文
    :param password: 明文密码
    :return:
    """
    return pwd_context.hash(password)


def create_access_token(subject: Union[str, Any], expires_delta: Optional[timedelta] = None):
    """
    生成token
    :param subject: 加密信息
    :param expires_delta: 有效时间
    :return:
    """
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(
            minutes=int(get_config("ACCESS_TOKEN_EXPIRE_MINUTES"))
        )
    to_encode = {"exp": expire, "sub": str(subject)}
    encoded_jwt = jwt.encode(to_encode, get_config("SECRET_KEY"), algorithm=get_config("ALGORITHM"))
    return encoded_jwt


def payload_token(token: str):
    """解token"""
    payload = jwt.decode(
        token, get_config("SECRET_KEY"), algorithms=[
            get_config("ALGORITHM")])
    return payload.get("sub")


@dataclass
class Response:
    errmsg: str = 'ok'
    data: Optional[Union[Dict, List]] = None
    code: int = 20000

    @property
    def serialization(self):
        return asdict(self)

